Lambda 関数を利用した EC2 Auto Scaling グループ所属インスタンスの AZ 別自動連番付けをやってみた
はじめに
テクニカルサポートの 片方 です。
Lambda 関数を利用した、EC2 Auto Scaling インスタンスの AZ 別自動連番をやってみました。
今回は、2 パターン作成してます。
例えば 2AZ 環境の場合は以下のように割り当てられます。
-
起動時間による番号の決定
古い起動時間の EC2 インスタンスから若い番号を割り当て -
AZ ごとの番号割り当て
ap-northeast-1a → 奇数番号(01, 03, 05...)
ap-northeast-1c → 偶数番号(02, 04, 06...)
3AZ の場合は以下のように割り当てられます。
-
起動時間による番号の決定
古い起動時間の EC2 インスタンスから若い番号を割り当て -
AZ ごとの番号割り当て
ap-northeast-1a → 01-1a, 02-1a, 03-1a
ap-northeast-1c → 01-1c, 02-1c, 03-1c
ap-northeast-1d → 01-1d, 02-1c, 03-1d
実装してみた
以下の順番で実装します。
- 実行ロール作成
- Lambda 関数作成
EC2 Auto Scaling の対象グループ内で起動されている EC2 インスタンスの判断は作成時に自動で付与される aws:autoscaling:groupName タグ記載の Values を基にしています。
実行ロール
信頼関係
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "lambda.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
}
アタッチするポリシー例
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "logs:CreateLogGroup",
"Resource": "arn:aws:logs:<region>:<account-id>:*"
},
{
"Effect": "Allow",
"Action": [
"logs:CreateLogStream",
"logs:PutLogEvents"
],
"Resource": [
"arn:aws:logs:<region>:<account-id>:log-group:/aws/lambda/<function-name>:*"
]
},
{
"Effect": "Allow",
"Action": [
"ec2:DescribeInstances",
"ec2:CreateTags",
"ec2:DescribeTags",
"autoscaling:DescribeAutoScalingGroups"
],
"Resource": "*"
}
]
}
※ 適宜修正してください。
Lambda 関数
Python 3.13 で作成しました。
実行ロールでは、既存のロールを使用するを選択し、先ほど作成したロールを指定します。
2AZ で実装する Lambda 関数例
import boto3
from datetime import datetime
import logging
logger = logging.getLogger()
logger.setLevel(logging.INFO)
def lambda_handler(event, context):
ec2_client = boto3.client('ec2')
try:
# AutoScalingグループのタグを持つEC2インスタンスを取得
instances = ec2_client.describe_instances(
Filters=[
{'Name': 'tag:aws:autoscaling:groupName', 'Values': ['xxxxxxx']},
{'Name': 'instance-state-name', 'Values': ['running']}
]
)
# AZごとにインスタンスを分類
az_instances = {
'ap-northeast-1a': [], # 奇数用
'ap-northeast-1c': [] # 偶数用
}
# インスタンスをAZごとに振り分け
for reservation in instances['Reservations']:
for instance in reservation['Instances']:
az = instance['Placement']['AvailabilityZone']
if az in az_instances:
az_instances[az].append({
'InstanceId': instance['InstanceId'],
'LaunchTime': instance['LaunchTime'],
'AZ': az
})
# 各AZでの処理
for az, az_list in az_instances.items():
if not az_list:
continue
# 起動時間でソート
sorted_instances = sorted(az_list, key=lambda x: x['LaunchTime'])
# AZに基づいて開始番号を決定
if az.endswith('a'):
# ap-northeast-1a は奇数
start_number = 1
increment = 2
else:
# ap-northeast-1c は偶数
start_number = 2
increment = 2
# インスタンスにタグ付け
for i, instance in enumerate(sorted_instances):
number = start_number + (i * increment)
instance_name = f"your-prefix-{number:02d}"
# 現在のタグを確認
current_tags = ec2_client.describe_tags(
Filters=[
{'Name': 'resource-id', 'Values': [instance['InstanceId']]},
{'Name': 'key', 'Values': ['Name']}
]
)
current_name = None
if current_tags['Tags']:
current_name = current_tags['Tags'][0]['Value']
# タグが異なる場合のみ更新
if current_name != instance_name:
ec2_client.create_tags(
Resources=[instance['InstanceId']],
Tags=[{'Key': 'Name', 'Value': instance_name}]
)
logger.info(f"Tagged instance {instance['InstanceId']} in {az} with {instance_name} (was {current_name})")
return {
'statusCode': 200,
'body': 'Successfully updated instance tags'
}
except Exception as e:
logger.error(f"Error: {str(e)}")
raise
3AZ で実装する Lambda 関数例
import boto3
from datetime import datetime
import logging
logger = logging.getLogger()
logger.setLevel(logging.INFO)
def lambda_handler(event, context):
ec2_client = boto3.client('ec2')
try:
# AutoScalingグループのタグを持つEC2インスタンスを取得
instances = ec2_client.describe_instances(
Filters=[
{'Name': 'tag:aws:autoscaling:groupName', 'Values': ['xxxxxxx']},
{'Name': 'instance-state-name', 'Values': ['running']}
]
)
# AZごとにインスタンスを分類
az_instances = {
'ap-northeast-1a': [],
'ap-northeast-1c': [],
'ap-northeast-1d': []
}
# インスタンスをAZごとに振り分け
for reservation in instances['Reservations']:
for instance in reservation['Instances']:
az = instance['Placement']['AvailabilityZone']
if az in az_instances:
az_instances[az].append({
'InstanceId': instance['InstanceId'],
'LaunchTime': instance['LaunchTime'],
'AZ': az
})
# 各AZでの処理
for az, az_list in az_instances.items():
if not az_list:
continue
# 起動時間でソート
sorted_instances = sorted(az_list, key=lambda x: x['LaunchTime'])
# AZサフィックスを取得 (例:1a, 1c, 1d)
az_suffix = az[-2:]
# インスタンスにタグ付け
for i, instance in enumerate(sorted_instances):
number = i + 1 # 1から始まる連番
instance_name = f"your-prefix-{number:02d}-{az_suffix}"
# 現在のタグを確認
current_tags = ec2_client.describe_tags(
Filters=[
{'Name': 'resource-id', 'Values': [instance['InstanceId']]},
{'Name': 'key', 'Values': ['Name']}
]
)
current_name = None
if current_tags['Tags']:
current_name = current_tags['Tags'][0]['Value']
# タグが異なる場合のみ更新
if current_name != instance_name:
ec2_client.create_tags(
Resources=[instance['InstanceId']],
Tags=[{'Key': 'Name', 'Value': instance_name}]
)
logger.info(f"Tagged instance {instance['InstanceId']} in {az} with {instance_name} (was {current_name})")
return {
'statusCode': 200,
'body': 'Successfully updated instance tags'
}
except Exception as e:
logger.error(f"Error: {str(e)}")
raise
※ 適宜修正してください。
特に、EC2 Auto Scaling 作成時に自動で付与される aws:autoscaling:groupName タグ記載の Values': ['xxxxxxx'] にはご自身の値を記載してください。
これで、実装は完了です。お疲れさまでした。
検証してみた
EC2 Auto Scaling を作成し、EC2 を起動させます。
それでは 2AZ の Lambda 関数をテストします。
成功したので、タグ付けされているか確認します。タグ付けされているので、成功です!
この後、02 と 03 が付与されている EC2 インスタンスを削除して、希望するキャパシティーを 6 台に変更します。
既存の 2 台を含め 6 台で起動されたので、再度 Lambda 関数を実行してタグの修正と追加されるか試します。
成功です!
続いて 3AZ の Lambda 関数も同様にテストします。
それでは 3AZ の Lambda 関数をテストします。
成功です!
この後適当に EC2 インスタンスを削除して、希望するキャパシティーを 9 台に変更します。
再度 Lambda 関数を実行してタグの修正と追加されるか試します。
成功です!
まとめ
Eventbridge を利用して EC2 インスタンスの起動などをトリガーに該当 Lambda 関数を呼び出す設定や、スケジュール機能を利用して定期実行すれば完全自動で付与可能です。
本ブログが誰かの参考になれば幸いです。
参考資料
- Auto Scaling グループとインスタンスにタグを付ける - Amazon EC2 Auto Scaling
- EC2 - Boto3 1.36.2 documentation
- AWS Lambda とは - AWS Lambda
アノテーション株式会社について
アノテーション株式会社は、クラスメソッド社のグループ企業として「オペレーション・エクセレンス」を担える企業を目指してチャレンジを続けています。「らしく働く、らしく生きる」のスローガンを掲げ、様々な背景をもつ多様なメンバーが自由度の高い働き方を通してお客様へサービスを提供し続けてきました。現在当社では一緒に会社を盛り上げていただけるメンバーを募集中です。少しでもご興味あれば、アノテーション株式会社WEBサイトをご覧ください。